use std::mem;

fn main() {
    println!("闭包作为输入参数");
    // Fn、FnMut 和 FnOnce 这些 trait 明确了闭包如何从周围的作用域中捕获变量。
    // Fn：表示捕获方式为通过引用（&T）的闭包
    // FnMut：表示捕获方式为通过可变引用（&mut T）的闭包
    // FnOnce：表示捕获方式为通过值（T）的闭包
    // 顺序之所以是这样，是因为 &T 只是获取了不可变的引用，&mut T 则可以改变变量，T 则是拿到了变量的所有权而非借用
    // 如果参数的类型说明是 Fn，那么不允许该闭包通过 &mut T 或 T 捕获变量

    fn apply<F>(f: F)
    where
        // 闭包没有输入值和返回值
        F: FnOnce(),
    {
        f();
    }

    fn apply_to_3<F>(f: F)
    where
        F: Fn(i32) -> i32,
    {
        f(3);
    }

    let greeting = "hello";
    let mut farewell = "goodbye".to_owned(); // 取得所有权的 String

    let dairy = || {
        // 通过引用捕获 greeting, 故需用 fn
        println!("I said {}", greeting);

        // farewell 在这被改变需要 fnmut
        farewell.push_str("!!!");
        println!("Then I screamed {}", farewell);
        // 销毁 farewell, 需要 fnonce 捕获所有权
        mem::drop(farewell);
    };

    apply(dairy);

    let double = |x| x * 2;
    println!("3 double: {:?}", apply_to_3(double));
}
